package com.sunseaypw.web.serverlet;

import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

// WebServlet注解表示这是一个Servlet，并映射到地址/:
//早期的Servlet需要在web.xml中配置映射路径，但最新Servlet版本只需要通过注解就可以完成映射。
//映射到/的Servlet比较特殊，它实际上会接收所有未匹配的路径，相当于/*
@WebServlet(urlPatterns = "/")
public class MainServerlet extends HttpServlet {    
    /* 一个Servlet类在服务器中只有一个实例，但对于每个HTTP请求，Web服务器会使用多线程执行请求。
        因此，一个Servlet的doGet()、doPost()等处理请求的方法是多线程并发执行的。
        如果Servlet中定义了字段，要注意多线程并发访问的问题;
        对于每个请求，Web服务器会创建唯一的HttpServletRequest和HttpServletResponse实例，
        因此，HttpServletRequest和HttpServletResponse实例只有在当前处理线程中有效，它们总是局部变量，不存在多线程共享的问题。
    因为浏览器发送请求的时候，还会有请求方法（HTTP Method）：即GET、POST、PUT等不同类型的请求。
    因此，要处理GET请求，我们要覆写doGet()方法；不覆写但有请求则走默认实现，会返回响应的HTTP响应码; */
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        //var webapp_ctxpath=req.getContextPath();//当前Webapp挂载的路径，对于ROOT来说，总是返回空字符串""；
        /*
            getMethod()：返回请求方法，例如，"GET"，"POST"；
            getRequestURI()：返回请求路径，但不包括请求参数，例如，"/hello"；
            getQueryString()：返回请求参数，例如，"name=Bob&a=1&b=2"；
            getParameter(name)：返回请求参数，GET请求从URL读取参数，POST请求从Body中读取参数；
            getContentType()：获取请求Body的类型，例如，"application/x-www-form-urlencoded"；
            getContextPath()：获取当前Webapp挂载的路径，对于ROOT来说，总是返回空字符串""；
            getCookies()：返回请求携带的所有Cookie；
            getHeader(name)：获取指定的Header，对Header名称不区分大小写；
            getHeaderNames()：返回所有Header名称；
            getInputStream()：如果该请求带有HTTP Body，该方法将打开一个输入流用于读取Body；
            getReader()：和getInputStream()类似，但打开的是Reader；
            getRemoteAddr()：返回客户端的IP地址；
            getScheme()：返回协议类型，例如，"http"，"https"；
        */
        req.setAttribute("content-type", "aaaaaa");        
        /*由于HTTP响应必须【先发送Header，再发送Body】
          所以，操作HttpServletResponse对象时，必须先调用设置Header的方法，最后调用发送Body的方法。
            setStatus(sc)：设置响应代码，默认是200；
            setContentType(type)：设置Body的类型，例如，"text/html"；
            setCharacterEncoding(charset)：设置字符编码，例如，"UTF-8"；
            setHeader(name, value)：设置一个Header的值；
            addCookie(cookie)：给响应添加一个Cookie；
            addHeader(name, value)：给响应添加一个Header，因为HTTP协议允许有多个相同的Header；
            写入响应时，需要通过getOutputStream()获取写入流，或者通过getWriter()获取字符流，二者只能获取其中一个。
            写入响应前，【无需设置setContentLength()】，因为底层服务器会根据写入的字节数自动设置，
                如果写入的数据量很小，实际上会先写入缓冲区，
                如果写入的数据量很大，服务器会自动采用Chunked编码让浏览器能识别数据结束符而不需要设置Content-Length头。
            但是，写入完毕后调用flush()却是必须的，因为大部分Web服务器都基于HTTP/1.1协议，会复用TCP连接。
                如果没有调用flush()，将导致缓冲区的内容无法及时发送到客户端。
            此外，写入完毕后千万不要调用close()，原因同样是因为会复用TCP连接，
                如果关闭写入流，将关闭TCP连接，使得Web服务器无法复用此TCP连接。
            写入完毕后对输出流调用flush()而不是close()方法！
        */
        // 设置响应类型:
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        /* 容器基于http协议已经在首次访问时生成并发送给客户端一个cookie 名为：JSESSIONID  值一定时唯一的确定一个访问来源
           基于Host头 标定 cookie作用域为 / 
           基于此JSESSIONID cookie的值唯一来源，在服务侧开辟一个集合，存储只属于此来源客户端的信息！！！
        */
        try{
            req.getSession().setAttribute("attr111", "session_6666666666");
            req.getSession().setAttribute("attr222", "session_8888888888");
        }catch(Exception ex){
            
        }
        // 获取输出流:
        PrintWriter pw = resp.getWriter();
        // 写入响应:
        pw.write("<h1>Hello, world 欢迎您!</h1>");
        // 最后不要忘记flush强制输出:
        pw.flush();
    }
}
